home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 September / Macworld (1998-09).dmg / Shareware World / Info / For Developers / MacZoop 1.8.3 / Required Classes / Z Sources / ZApplication.cpp < prev    next >
Text File  |  1998-07-10  |  51KB  |  1,949 lines

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            MacZoop - "the framework for the rest of us"         
  5. *
  6. *
  7. *
  8. *            ZApplication.cpp    -- the application object
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *            © 1996, Graham Cox
  15. *
  16. *
  17. *
  18. *
  19. *************************************************************************************************/
  20.  
  21.  
  22. #include    "MacZoop.h"
  23. #include    "ProjectSettings.h"
  24. #include    "ZEventHandler.h"
  25. #include    "ZWindow.h"
  26. #include    "ZPrinter.h"
  27. #include    "ZUndoTask.h"
  28.  
  29. #if APPEARANCE_MGR_AWARE
  30. #include    <Appearance.h>
  31. #endif
  32.  
  33. #if _USE_NAVIGATION_SERVICES
  34. #include    <Navigation.h>
  35. #endif
  36.  
  37. #include    <StandardFile.h>
  38. #include    <Notification.h>
  39.  
  40. // gApplication is the global application object. There is only one, naturally.
  41.  
  42. ZApplication*    gApplication = NULL;
  43.  
  44. // globals inited by this class
  45.  
  46. ZMenuBar*        gMenuBar = NULL;    // the main menubar object
  47. OSType            gAppSignature;        // the application's signature, obtained from 'BNDL', etc.    
  48. tMacInfo        gMacInfo;            // common gestalt results
  49. RgnHandle        gUtilRgn = NULL;    // general purpose region, handy as temp variable. Do NOT dispose!
  50. ZPrefsFile*        gPrefsFile = NULL;    // NOT inited unless your application subclass does it!    
  51.  
  52. // static functions used by the application object
  53.  
  54. static pascal long     ZGrowFunc( Size bytesShort );
  55. static GrowZoneUPP    gGZFunc = NewGrowZoneProc( ZGrowFunc );
  56.  
  57. #if _USE_NAVIGATION_SERVICES
  58. static pascal void    ZNavEventCallback(     NavEventCallbackMessage cbMessage,
  59.                                         NavCBRecPtr    cbParams,
  60.                                         NavCallBackUserData    cbData );
  61. NavEventUPP        gNavEventHandler = NewNavEventProc( ZNavEventCallback );
  62. #endif
  63.  
  64. // note: ZApplication is NEVER instantiated from a stream- to ensure this but keep
  65. // special cases out of the class registry, etc, we jig the construction function
  66. // to simply return gApplication.
  67.  
  68. ZObject*     CF_ZApplication()
  69. {
  70.     return gApplication;
  71. }
  72.  
  73.  
  74. extern ZObject* CF_ZObjectList();
  75.  
  76. /*--------------------------------***  CONSTRUCTOR  ***---------------------------------*/
  77.  
  78. ZApplication::ZApplication()
  79.     : ZCommander( NULL )
  80. {
  81.     long    qdFeatures;
  82.     OSErr    theErr;
  83.     
  84.     classID = CLASS_ZApplication;
  85.     
  86.     gUtilRgn = NewRgn();
  87.     done = FALSE;
  88.     phase = kInitialising;
  89.     zEH = NULL;
  90.     curUndoTask = NULL;
  91.     itsPrinter = NULL;
  92.     
  93.     appResRefNum = CurResFile();
  94.     
  95.     // set initial filetypes list to a zero handle
  96.     
  97.     itsFileTypes = (FTypeListHdl) NewHandleClear( sizeof( FTypeList ) - sizeof( OSType ));
  98.         
  99.     shortageFund = NULL;
  100.     memIsShort = FALSE;
  101.     userHasSeenAlert = FALSE;
  102.     
  103.     // check to see if we are running on a colour Mac
  104.     
  105.     theErr = Gestalt( gestaltQuickdrawFeatures, &qdFeatures );
  106.     gMacInfo.supportsColour = ((theErr == noErr) && (qdFeatures & 1));
  107.     
  108.     // check for the drag manager
  109.     
  110.     theErr = Gestalt( gestaltDragMgrAttr, &qdFeatures );
  111.     gMacInfo.hasDragManager = ((theErr == noErr) && (qdFeatures & 1));
  112.     
  113.     // check for a FPU
  114.     
  115.     theErr = Gestalt( gestaltFPUType, &qdFeatures );
  116.     gMacInfo.hasFPU = ((theErr == noErr) && ((qdFeatures & 1) == 0));
  117.     
  118.     // check for applescript
  119.     
  120.     theErr = Gestalt( gestaltAppleEventsAttr, &qdFeatures );
  121.     gMacInfo.hasAppleEvents = ((theErr == noErr) && (qdFeatures & 1));
  122.     
  123.     // check for QuickTime™
  124.     
  125.     theErr = Gestalt( gestaltQuickTimeVersion, &qdFeatures );
  126.     gMacInfo.hasQuickTime = ((theErr == noErr) && (qdFeatures > 0));
  127.  
  128.     // check for ICM
  129.     
  130.     theErr = Gestalt( gestaltCompressionMgr, &qdFeatures );
  131.     gMacInfo.hasImgCompressionMgr = (theErr == noErr);
  132.  
  133.     // check for appearance manager
  134.     
  135.     #if APPEARANCE_MGR_AWARE
  136.         theErr = Gestalt( gestaltAppearanceAttr, &qdFeatures );
  137.         gMacInfo.hasAppearanceMgr = (( theErr == noErr ) && ( qdFeatures & 1 ));
  138.         
  139.         #if __powerc
  140.         if ( gMacInfo.hasAppearanceMgr )
  141.             gMacInfo.hasAppearanceMgr = ((long)  CreateRootControl != kUnresolvedCFragSymbolAddress );
  142.         #endif
  143.     
  144.     #else
  145.         gMacInfo.hasAppearanceMgr = FALSE;
  146.     
  147.     #endif
  148.     
  149.     // what is the system version?
  150.     
  151.     theErr = Gestalt( gestaltSystemVersion, &qdFeatures );
  152.     gMacInfo.systemVersion = LoWord( qdFeatures );
  153.     
  154.     // check for navigation services:
  155.     
  156.     #if _USE_NAVIGATION_SERVICES
  157.     if ( gMacInfo.systemVersion >= 0x0755 )
  158.         gMacInfo.hasNavigationServices = NavServicesAvailable();
  159.     #else
  160.         gMacInfo.hasNavigationServices = FALSE;
  161.     #endif
  162.         
  163.     // initialise the animating cursors, and set the
  164.     // animated watch cursor going.
  165.     
  166.     AppCursorInit();
  167.     gApplication = this;
  168. }
  169.  
  170. /*--------------------------------***  DESTRUCTOR  ***---------------------------------*/
  171.  
  172.  
  173. ZApplication::~ZApplication()
  174. {
  175.     if ( gPrefsFile )
  176.         ForgetObject( gPrefsFile );
  177.     
  178.     if ( zEH )
  179.         ForgetObject( zEH );
  180.         
  181.     if ( itsPrinter )
  182.         ForgetObject( itsPrinter );
  183.         
  184.     if ( curUndoTask )
  185.         ForgetObject( curUndoTask );
  186.         
  187.     if ( itsFileTypes )
  188.         DisposeHandle((Handle) itsFileTypes);
  189.         
  190.     ForgetObject( gMenuBar );
  191.     StopCursorAnimation();
  192. }
  193.  
  194.  
  195. /*--------------------------------***  INITMACZOOP  ***---------------------------------*/
  196. /*    
  197.  
  198. Initialises the application, makes the event handler object and the menubar
  199.  
  200. ----------------------------------------------------------------------------------------*/
  201.  
  202. void        ZApplication::InitMacZoop( const short numMasterBlocks )
  203. {
  204.     if ( phase == kInitialising )
  205.     {
  206.         // init the mac toolbox, memory mangler etc.
  207.         
  208.         RegisterClasses();
  209.         InitMacApplication( numMasterBlocks );
  210.         
  211.         // create the memory shortage fund
  212.         
  213.         FailNIL( shortageFund = NewHandle( kShortageFundSize ));
  214.         SetGrowZone( gGZFunc );
  215.         
  216.         SetWatchCursor();
  217.         
  218.         // make the event handler object
  219.         
  220.         MakeEventHandler();
  221.     
  222.         // make the clipboard object
  223.         
  224.         MakeClipboard();
  225.         
  226.         // read any prefs that the user may have set up (default does nothing). A typical
  227.         // thing to do here is to simply instantiate a ZPrefsFile object and assign it to
  228.         // <gPrefsFile>. You may also want to open/read the file! (use of data or resource
  229.         // fork is up to you, so ZPrefsFile does not automatically perform the open step).
  230.         
  231.         ReadPrefs();
  232.         
  233.         // initialise menubar and make printer object
  234.         
  235.         InitMenuBar();
  236.         MakePrinter();
  237.         
  238.         // preload the navigation services if required, to make use of these faster later on.
  239.         
  240.         #if _USE_NAVIGATION_SERVICES
  241.         if ( gMacInfo.hasNavigationServices )
  242.             FailOSErr( NavLoad());
  243.         #endif
  244.         
  245.         // call user-function as last part of initialisation- default
  246.         // does nothing but can be overridden to do further set up.
  247.         
  248.         StartUp();
  249.         
  250.         // update the menubar- note that this is not drawn until StartUp completes,
  251.         // since we allow the programmer the flexibility to use StartUp to create & install
  252.         // dynamic menus, etc. if they want.
  253.         
  254.         gMenuBar->UpdateMenuBar();
  255.         
  256.         // the cursor is not reset here- we let the main event loop do that so that if
  257.         // events are initially available, the cursor keeps right on animating until
  258.         // the user gets a chance to do anything.
  259.         
  260.         phase = kRunning;
  261.     }
  262. }
  263.  
  264. /*-----------------------------***  INITMACAPPLICATION  ***-----------------------------*/
  265. /*    
  266.  
  267. Initialises the mac toolbox and memory manager
  268.  
  269. ----------------------------------------------------------------------------------------*/
  270.  
  271.     
  272. void        ZApplication::InitMacApplication( const short numMasterBlocks )
  273. {
  274.     // chant the "Macintosh mantra"...
  275.     
  276.     InitGraf( &qd.thePort );
  277.     InitFonts();
  278.     InitWindows();
  279.     InitMenus();
  280.     TEInit();
  281.     InitDialogs( NULL );
  282.     
  283.     // clear out the event queue in case any stray clicks or keypresses are left there
  284.     
  285.     FlushEvents( everyEvent, 0 );
  286.  
  287.     // see if the program can run on this Mac- if not, we show an alert and exit straight away
  288.     
  289.     if (! CheckCanRun())
  290.     {
  291.         StopCursorAnimation();
  292.         (void) Alert( kCantRunAlertID, NULL );
  293.         ExitToShell();
  294.     }    
  295.     else
  296.     {
  297.         // give yourself enough memory. For a bigger app, you may need to call MoreMasters a few
  298.         // more times. The parameter to this method sets the number of times MoreMasters is
  299.         // called, defaulting to 8, which gives 8 x 64 = 512 handles.
  300.         
  301.         MaxApplZone();
  302.         
  303.         short n = numMasterBlocks;
  304.         
  305.         while( n-- )
  306.             MoreMasters();
  307.             
  308.         // if we want appearance, register the app:
  309.         
  310.     #if APPEARANCE_MGR_AWARE
  311.     
  312.         FailOSErr( RegisterAppearanceClient());
  313.     
  314.     #endif
  315.             
  316.         // set up <gAppSignature>, either from BNDL resource, or from constant according
  317.         // to the user's project settings.
  318.         
  319.     #if USE_SIGNATURE_FROM_BNDL
  320.         
  321.         OSType**    bndlHand = (OSType**) GetResource( 'BNDL', 128 );
  322.         
  323.         if ( bndlHand )
  324.         {
  325.             gAppSignature = **bndlHand;
  326.             ReleaseResource((Handle) bndlHand );
  327.             
  328.             #if CHECK_FREF_RESOURCE_TYPES
  329.             
  330.             // first look for an 'open' resource ID=128. If there is one, we use it in
  331.             // preference to the FREF's, since there is a slightly different meaning. An 'open'
  332.             // resource can be treated as a list of OSTypes if you ignore the first 8 bytes.
  333.             
  334.             bndlHand = (OSType**) GetResource( 'open', 128 );
  335.             
  336.             if ( bndlHand )
  337.             {
  338.                 // we have an 'open' resource, so just copy the data (ignoring first 8 bytes)
  339.                 // to the file types list. Warning: This technique does not check for duplicates,
  340.                 // either in the resource or in the types list.
  341.                 
  342.                 long    bc = GetHandleSize((Handle) bndlHand );
  343.                 
  344.                 SetHandleSize((Handle) itsFileTypes, bc );
  345.                 FailMemError();
  346.                 
  347.                 BlockMoveData((Ptr) *bndlHand, (Ptr) *itsFileTypes, bc );
  348.                 ReleaseResource((Handle) bndlHand );
  349.             }
  350.             else
  351.             {
  352.                 // scan the 'FREF' resources and build the <itsFileTypes> list. We do NOT add
  353.                 // types of 'APPL', 'cdev', 'INIT', or 'rdev'- if you want to display such
  354.                 // files in the Open dialog, you have to call AddFileType() for these types directly.
  355.                 // This is a feature to prevent weird behaviour in the typical case.
  356.                 
  357.                 // how many 'FREF's do we have?
  358.                 
  359.                 short    i, fc = Count1Resources( 'FREF' );
  360.                 
  361.                 for( i = 1; i <= fc; i++ )
  362.                 {
  363.                     bndlHand = (OSType**) GetIndResource( 'FREF', i );
  364.                 
  365.                     if ( bndlHand )
  366.                     {
  367.                         // check if this is one of our 'forbidden' types:
  368.                         
  369.                         if ( **bndlHand != 'APPL'    &&
  370.                              **bndlHand != 'APPC'    &&
  371.                              **bndlHand != 'APPD'    &&
  372.                              **bndlHand != 'cdev'    &&
  373.                              **bndlHand != 'rdev'    &&
  374.                              **bndlHand != 'INIT' )
  375.                             AddFileType( **bndlHand );
  376.                         
  377.                         ReleaseResource((Handle) bndlHand );
  378.                     }
  379.                 }
  380.             }
  381.             
  382.             #endif    
  383.         }
  384.         else
  385.             gAppSignature = kUnknownSignature;
  386.     #else
  387.         gAppSignature = kApplicationSignature;
  388.     #endif
  389.     
  390.         (*itsFileTypes)->appSignature = gAppSignature;
  391.     }
  392. }
  393.  
  394. /*--------------------------------***  CHECKCANRUN  ***---------------------------------*/
  395. /*    
  396.  
  397. Examine the machine environment to see if this will run on this Mac. By default, it can
  398. on System 7 or later.
  399.  
  400. ----------------------------------------------------------------------------------------*/
  401.  
  402. Boolean        ZApplication::CheckCanRun()
  403. {
  404.     // Returning FALSE will cause the program to
  405.     // immediately show an alert and quit.
  406.     
  407.     // by default, we can run on any Mac with System 7.0 or later.
  408.     
  409.     return( gMacInfo.systemVersion >= 0x0700 );    
  410. }
  411.  
  412. #pragma mark -
  413. /*----------------------------------***  GETNAME  ***-----------------------------------*/
  414. /*    
  415. Get the user-visible name of the application, as seen in the Finder. This works even if
  416. the user has renamed the app, since it finds the filename.
  417. ----------------------------------------------------------------------------------------*/
  418.  
  419.  
  420. void        ZApplication::GetName( Str255 appName )
  421. {
  422.     StringPtr sp = LMGetCurApName();
  423.     CopyPString( sp, appName );
  424. }
  425.  
  426. /*------------------------------------***  RUN  ***-------------------------------------*/
  427. /*    
  428.  
  429. Fetch events and handle them until the user quits
  430.  
  431. ----------------------------------------------------------------------------------------*/
  432.  
  433. void        ZApplication::Run()
  434. {
  435.     // runs the show by asking the event object to get events and handle them. This goes on
  436.     // forever until done is set to TRUE.
  437.     
  438.     if ( phase == kRunning )
  439.     {
  440.         while(! done)
  441.         {
  442.             try
  443.             {
  444.                 // repeatedly get an event, handle an event
  445.                 
  446.                 Process1Event();
  447.                 
  448.                 // deal with the memory shortage situation, if one has arisen as
  449.                 // a result of the last event.
  450.                 
  451.                 CheckLowMemory();
  452.             }
  453.             catch( OSErr theErr )
  454.             {
  455.                 // if here, an exception was thrown. If the error was userCanceledErr,
  456.                 // the alert is not shown, since the exception is legitimate. Thus for
  457.                 // many operations, you need only throw a userCanceledErr exception to
  458.                 // simply invoke the cancel.
  459.                 
  460.                 StopCursorAnimation();
  461.                 HandleError( theErr );
  462.                 
  463.                 // the buck stops here- no exceptions will be thrown beyond this point.
  464.             }
  465.             // stop any animating cursors. This means that a lengthy process need
  466.             // only set the cursor going and can then forget about it. When the
  467.             // app resumes handling events, it will be automatically cancelled.
  468.             
  469.             StopCursorAnimation();
  470.         }
  471.         done = TRUE;
  472.         phase = kQuitting;
  473.     }
  474. }
  475.  
  476.  
  477. /*-----------------------------***  CHECKLOWMEMORY  ***---------------------------------*/
  478. /*    
  479. check if a low memory situation has arisen. If so, inform the user and manage the re-
  480. plenishment of the shortage fund.
  481. ----------------------------------------------------------------------------------------*/
  482.  
  483. void        ZApplication::CheckLowMemory()
  484. {
  485.     if ( memIsShort )
  486.     {
  487.         // some of the shortage fund was used. Try to replenish it:
  488.         
  489.         memIsShort = FALSE;
  490.         
  491.         Handle temp = NewHandle( kShortageFundSize );
  492.         
  493.         // if that resets memIsShort, then the grow zone func was called, so we
  494.         // are not in the clear yet. However, if the grow zone func wasn't
  495.         // called, then we can safely get rid of that handle and replace it with
  496.         // this one
  497.         
  498.         if ( memIsShort )
  499.         {
  500.             if ( temp )
  501.                 DisposeHandle( temp );
  502.             
  503.             // couldn't replenish the fund, so if the user hasn't seen the
  504.             // warning yet, show it now.
  505.             
  506.             if (! userHasSeenAlert)
  507.             {
  508.                 StopCursorAnimation();
  509.                 (void) NotifyAlert( kMemoryLowAlertID );
  510.                 userHasSeenAlert = TRUE;
  511.             }
  512.             
  513.             // if memIsShort is TRUE, it affects UpdateMenus such that New and Open are
  514.             // greyed out. This is an attempt to stop the user creating things that will
  515.             // eat up even more memory. You might want to use the same technique for commands
  516.             // of your own that may allocate lots of memory. For this reason, memIsShort is
  517.             // a public member.
  518.         }
  519.         else
  520.         {
  521.             // fund replenished, so get rid of any remaining fund and replace it
  522.             // with the newly allocated shortage fund.
  523.             
  524.             DisposeHandle( shortageFund );
  525.             shortageFund = temp;
  526.             userHasSeenAlert = FALSE;
  527.         }
  528.     }
  529. }
  530.  
  531.  
  532. /*------------------------------***  MEMORYSHORTAGE  ***--------------------------------*/
  533. /*    
  534.  
  535. called from the growzone proc when memory needs to be freed. This releases some or all
  536. of the shortage fund, but you can override it to make additional sacrifices if need be.
  537. ----------------------------------------------------------------------------------------*/
  538.  
  539. Boolean        ZApplication::MemoryShortage( const Size bytesShort )
  540. {
  541.     // this is called when the memory manager gets into dire straits. We can free some or all
  542.     // of our emergency fund to satisfy the request. If we succeed, we return TRUE, else FALSE.
  543.     
  544.     Size    fundSize = 0;
  545.     Handle    gzHandle;
  546.     
  547.     // get the handle the mem manager is dealing with at the moment. This is important since
  548.     // this might be the shortage fund itself. If it is, we can't resize it, so we really
  549.     // are in deep do-do. In this case, we flag memIsShort and hope the user will not ignore
  550.     // the message!
  551.     
  552.     gzHandle = GZSaveHnd();
  553.     
  554.     if ( gzHandle != shortageFund )
  555.     {
  556.         fundSize = GetHandleSize( shortageFund );
  557.         
  558.         // release all or some of the memory to try and satisfy the request
  559.         
  560.         if ( fundSize <= bytesShort )
  561.             SetHandleSize( shortageFund, 0 );
  562.         else
  563.             SetHandleSize( shortageFund, fundSize - bytesShort );
  564.     }
  565.     // flag the shortage so we can inform the user
  566.     
  567.     memIsShort = TRUE;
  568.     
  569.     // did we actually manage to free the requested amount?
  570.     
  571.     return( fundSize > bytesShort );
  572. }
  573.  
  574.  
  575. /*------------------------------***  PROCESS1EVENT  ***---------------------------------*/
  576. /*    
  577. get an event, dispatch an event, get an event, dispatch...
  578. ----------------------------------------------------------------------------------------*/
  579.  
  580. void        ZApplication::Process1Event()
  581. {
  582.     EventRecord        theEvent;
  583.     
  584.     zEH->GetAnEvent( &theEvent );
  585.     zEH->DispatchAnEvent( &theEvent );
  586. }
  587.  
  588.  
  589. /*------------------------------***  PROCESS1EVENT  ***---------------------------------*/
  590. /*    
  591. dispatch an event that was fetched externally, for example by a library such as Nav
  592. Services.
  593. ----------------------------------------------------------------------------------------*/
  594.  
  595. void        ZApplication::Process1Event( EventRecord* anExternalEvent )
  596. {
  597.     zEH->DispatchAnEvent( anExternalEvent );
  598. }
  599.  
  600.  
  601. /*-----------------------------***  GETCURRENTEVENT  ***--------------------------------*/
  602. /*
  603. return the current event in progress, with FALSE if it was a null event, else TRUE.    
  604. ----------------------------------------------------------------------------------------*/
  605.  
  606. Boolean        ZApplication::GetCurrentEvent( EventRecord* anEvent )
  607. {
  608.     zEH->GetLatestEvent( anEvent );
  609.     
  610.     return( anEvent->what != nullEvent );
  611. }
  612.  
  613.  
  614. /*--------------------------------***  GETCLICKS  ***-----------------------------------*/
  615. /*    
  616. return the number of clicks counted by the event handler in the same place. Returns 1 for
  617. single click, 2 for double, 3 for triple, etc.
  618. ----------------------------------------------------------------------------------------*/
  619.  
  620. short        ZApplication::GetClicks()
  621. {
  622.     return zEH->GetClicks();
  623. }
  624.  
  625.  
  626. /*-------------------------------***  INBACKGROUND  ***---------------------------------*/
  627. /*    
  628. return TRUE if the application is in the background, FALSE in foreground.
  629. ----------------------------------------------------------------------------------------*/
  630.  
  631. Boolean        ZApplication::InBackground()
  632. {
  633.     return zEH->InBackground();
  634. }
  635.  
  636.  
  637. /*-----------------------------------***  QUIT  ***-------------------------------------*/
  638. /*    
  639.  
  640. close all of the windows. If successful, delete the application and return TRUE
  641.  
  642. ----------------------------------------------------------------------------------------*/
  643.  
  644. Boolean        ZApplication::Quit()
  645. {
  646.     if ( phase == kQuitting )
  647.     {
  648.         CloseAll();
  649.         
  650.         // the Quit can be abandoned by resetting <done> to FALSE. If this
  651.         // has not occurred, then truly say goodbye.
  652.         
  653.         if ( done )
  654.         {
  655.             #if _USE_NAVIGATION_SERVICES
  656.             if ( gMacInfo.hasNavigationServices )
  657.                 FailOSErr( NavUnload());
  658.             #endif
  659.             
  660.             #if APPEARANCE_MGR_AWARE
  661.             
  662.             (void) UnregisterAppearanceClient();
  663.             
  664.             #endif
  665.             
  666.             ShutDown();
  667.             ForgetThis();
  668.         }
  669.     }
  670.     return done;
  671. }
  672.  
  673.  
  674. /*-------------------------------***  REQUESTQUIT  ***----------------------------------*/
  675. /*    
  676.  
  677. ask the application to quit. This is called by choosing Quit from the File menu, for example.
  678.  
  679. ----------------------------------------------------------------------------------------*/
  680.  
  681.  
  682. void        ZApplication::RequestQuit()
  683. {
  684.     done = TRUE;
  685. }
  686.  
  687. /*----------------------------***  HANDLEAPPLEEVENT  ***--------------------------------*/
  688. /*    
  689. Handle the four required apple events. If you override this to handle your own apple
  690. events, be sure to call the inherited method for everything else.
  691. ----------------------------------------------------------------------------------------*/
  692.  
  693. void        ZApplication::HandleAppleEvent(    AEEventClass aeClass, AEEventID aeID,
  694.                                             AppleEvent* aeEvt, AppleEvent* reply )
  695. {
  696.     if ( aeClass == kCoreEventClass )
  697.     {
  698.         FSSpec        aFile;
  699.         AEDescList    docList;
  700.         long        i, n;
  701.         AEKeyword    keyWrd;
  702.         DescType    retType;
  703.         Size        actualSize;
  704.         FInfo        fi;
  705.         
  706.         switch ( aeID )
  707.         {
  708.             case kAEReopenApplication:
  709.                 if ( GetFrontWindow())
  710.                     break;
  711.                 // fall through to normal open app event case if there are no windows:
  712.             case kAEOpenApplication:
  713.                 #if MAKE_UNTITLED_STARTUP_WINDOW
  714.                 OpenNewWindow();
  715.                 #endif
  716.                 break;
  717.             
  718.             case kAEOpenDocuments:
  719.                 FailOSErr( AEGetParamDesc( aeEvt, keyDirectObject, typeAEList, &docList ));    
  720.                 FailOSErr( AECountItems( &docList, &n ));
  721.                 
  722.                 // get each document and open it
  723.                 try
  724.                 {
  725.                     for (i = 1; i <= n; i++)
  726.                     {
  727.                         FailOSErr( AEGetNthPtr( &docList,
  728.                                                 i, 
  729.                                                 typeFSS, 
  730.                                                 &keyWrd,
  731.                                                 &retType, 
  732.                                                 &aFile, 
  733.                                                 sizeof( FSSpec ), 
  734.                                                 &actualSize ));
  735.                         
  736.                         FailOSErr( FSpGetFInfo( &aFile, &fi ));
  737.                         
  738.                         // open the file into a window and select it, provided
  739.                         // it really is one we can open. If the file is a prefs file, call up
  740.                         // the kDoPreferences command:
  741.                         
  742.                         if ( fi.fdType == 'pref' )
  743.                             HandleCommand( kCmdDoPreferences );
  744.                         else
  745.                         {
  746.                             if ( CanOpenFileType( fi.fdType ))
  747.                                 OpenFile( aFile, fi.fdType, ( fi.fdFlags & kIsStationery ) == kIsStationery );
  748.                         }
  749.                     }
  750.                 }
  751.                 catch( OSErr err )
  752.                 {
  753.                     AEDisposeDesc( &docList );
  754.                     
  755.                     throw err;
  756.                 }
  757.                 FailOSErr( AEDisposeDesc( &docList ));
  758.                 break;
  759.             
  760.             case kAEPrintDocuments:
  761.                 FailOSErr( AEGetParamDesc( aeEvt, keyDirectObject, typeAEList, &docList ));    
  762.                 FailOSErr( AECountItems( &docList, &n ));
  763.                 
  764.                 // ask the application to show the page setup dialog
  765.                 
  766.                 if ( n > 0 )
  767.                     DoPageSetup();
  768.                 
  769.                 // get each document passed by the finder
  770.                 
  771.                 try
  772.                 {
  773.                     for (i = 1; i <= n; i++)
  774.                     {
  775.                         FailOSErr( AEGetNthPtr( &docList,
  776.                                                 i, 
  777.                                                 typeFSS, 
  778.                                                 &keyWrd,
  779.                                                 &retType, 
  780.                                                 &aFile, 
  781.                                                 sizeof( FSSpec ), 
  782.                                                 &actualSize ));
  783.                         
  784.                         FailOSErr( FSpGetFInfo( &aFile, &fi ));
  785.                         
  786.                         // open the file into a window and select it
  787.                         
  788.                         if ( CanOpenFileType( fi.fdType ))
  789.                         {
  790.                             OpenFile( aFile, fi.fdType, ( fi.fdFlags & kIsStationery ) == kIsStationery );
  791.                         
  792.                             // update the window (not strictly needed, but looks better)
  793.                         
  794.                             zEH->HandleWindowUpdate( mostRecent->GetMacWindow());
  795.                         
  796.                             // ask the application to print the document
  797.                         
  798.                             DoPrint();
  799.                         
  800.                             // that done, we can close the window and move on to the next
  801.                         
  802.                             mostRecent->Close( GetPhase());
  803.                         }
  804.                     }
  805.                 }
  806.                 catch( OSErr err )
  807.                 {
  808.                     AEDisposeDesc( &docList );
  809.                     
  810.                     throw err;
  811.                 }
  812.                 
  813.                 FailOSErr( AEDisposeDesc( &docList ));
  814.                 break;
  815.             
  816.             case kAEQuitApplication:
  817.                 RequestQuit();
  818.                 break;
  819.             
  820.             default:
  821.                 ZCommander::HandleAppleEvent( aeClass, aeID, aeEvt, reply );
  822.                 break;    
  823.         }
  824.     }
  825.     else
  826.         ZCommander::HandleAppleEvent( aeClass, aeID, aeEvt, reply );
  827. }
  828.  
  829. /*-------------------------------***  HANDLEERROR  ***----------------------------------*/
  830. /*    
  831. display an alert indicating the error (some errors do nothing, likewise noErr is ignored)
  832. ----------------------------------------------------------------------------------------*/
  833.  
  834. void        ZApplication::HandleError( OSErr theErr )
  835. {
  836.     // handles the error passed by displaying an alert. This is called by the exception
  837.     // handler for the application, but you can call it at any time. Some errors are "silent"
  838.     // in that the exception does not result in a message. These include userCanceled, kSilent
  839.     // Err, and aeEventNotHandled. Override this if you want to handle errors differently.
  840.     
  841.     gMenuBar->SetTitleHilite( 0, FALSE );
  842.     
  843.     if ( theErr != userCanceledErr &&
  844.          theErr != kSilentErr       &&
  845.          theErr != noErr            &&
  846.          theErr != errAEEventNotHandled )
  847.     {
  848.         StringHandle    errExpH;    
  849.         Str255            errMsgStr;
  850.         Str31            errExplStr;
  851.         Str15            errIDStr;
  852.         
  853.         NumToString( theErr, errIDStr );
  854.         
  855.         // try to build a meaningful error message by looking for an 'Estr'
  856.         // resource with the same iD as the error. If found, this is concatenated
  857.         // onto the generic error stub and displayed. If not found, the default
  858.         // explanation "an error occurred" is used.
  859.         
  860.         GetIndString( errMsgStr, 128, 10 );
  861.         
  862.         errExpH = (StringHandle) GetResource( 'Estr', theErr );
  863.         
  864.         if ( errExpH )
  865.         {
  866.             ConcatPStrings( errMsgStr, *errExpH );     
  867.             ReleaseResource((Handle) errExpH );
  868.         }
  869.         else
  870.         {
  871.             GetIndString( errExplStr, 128, 11 );
  872.             ConcatPStrings( errMsgStr, errExplStr );
  873.         }
  874.         
  875.         ParamText( errIDStr, errMsgStr, NULL, NULL );
  876.         (void) NotifyAlert( kExceptionAlertID, ntMinimalAlert );
  877.     }
  878. }
  879.  
  880.  
  881. /*------------------------***  WAITAPPLICATIONFOREGROUND  ***---------------------------*/
  882. /*    
  883. handle events until the application is not in the background. Used by NotifyAlert().
  884. ----------------------------------------------------------------------------------------*/
  885.  
  886. void        ZApplication::WaitApplicationForeground()
  887. {
  888.     while( InBackground())
  889.         Process1Event();
  890. }
  891.  
  892.  
  893. #pragma mark -
  894.  
  895. /*------------------------------***  HANDLECOMMAND  ***---------------------------------*/
  896. /*    
  897.  
  898. Handle commands (menu items) that apply to the application as a whole, like Quit.
  899. ----------------------------------------------------------------------------------------*/
  900.  
  901. void        ZApplication::HandleCommand( const long aCmd )
  902. {
  903.     // handle commands at the application level. This includes quit, new, open, etc.
  904.     
  905.     FSSpec        aFile;
  906.     OSType        anFType;
  907.     FInfo        fi;
  908.     
  909.     switch( aCmd )
  910.     {
  911.         case kCmdAbout:
  912.             AboutBox();
  913.             break;
  914.         
  915.         case kCmdNew:
  916.             OpenNewWindowType( kDefaultWindowType );    // open a new "untitled" window
  917.             break;
  918.         
  919.         case kCmdOpen:
  920.             if ( PickFile( &aFile, &anFType ))            // choose a file
  921.             {
  922.                 // is it a stationery (template) file?
  923.                 
  924.                 FSpGetFInfo( &aFile, &fi );
  925.                 OpenFile( aFile, anFType, ( fi.fdFlags & kIsStationery ) == kIsStationery );            // open the file into a window
  926.             }
  927.             break;
  928.         
  929.         case kCmdQuit:
  930.             RequestQuit();                            // the app should now quit
  931.             break;
  932.         
  933.         case kCmdPageSetup:
  934.             DoPageSetup();
  935.             break;
  936.         
  937.         case kCmdPrint:
  938.             DoPrint();
  939.             break;
  940.         
  941.         case kCmdUndo:
  942.             if ( curUndoTask )    // undo item available?
  943.             {
  944.                 SetWatchCursor();
  945.                 if (curUndoTask->IsUndone())        // undo or redo?
  946.                     curUndoTask->Redo();            // redo the task
  947.                 else
  948.                     curUndoTask->Undo();            // undo the task
  949.             }
  950.             break;
  951.             
  952.         case kCmdDoPreferences:
  953.             DoPreferences();
  954.             break;
  955.     }
  956. }
  957.  
  958.  
  959. /*------------------------------***  HANDLECOMMAND  ***---------------------------------*/
  960.  
  961. void        ZApplication::HandleCommand( const short menuID, const short itemID )
  962. {
  963.     GrafPtr        savePort;
  964.     Str255        daName;
  965.     
  966.     if ( menuID == kAppleMenuID &&
  967.          itemID > 2 )
  968.     {
  969.         // open desk accessories.
  970.         GetMenuItemText( GetMenuHandle( menuID ), itemID, daName );
  971.         GetPort( &savePort );
  972.         OpenDeskAcc( daName );
  973.         SetPort( savePort );
  974.     }
  975. }
  976.  
  977.  
  978. /*-------------------------------***  UPDATEMENUS  ***----------------------------------*/
  979. /*    
  980.  
  981. enable the menu commands that the app can handle at the moment.
  982.  
  983. ----------------------------------------------------------------------------------------*/
  984.  
  985. void    ZApplication::UpdateMenus()
  986. {
  987.     // enable the menu commands that pertain to the application. This includes "New",
  988.     // "Quit" and "About" amongst others
  989.     
  990.     // apple menu
  991.     
  992.     gMenuBar->EnableCommand( kCmdAbout );
  993.         
  994.     // file menu
  995.     
  996.     // if memory is currently short, do not enable new or open, since those are the
  997.     // commands that are likely to allocate a lot more memory, which we do not have.
  998.     
  999.     if (! memIsShort)
  1000.     {
  1001.         gMenuBar->EnableCommand( kCmdNew );
  1002.         gMenuBar->EnableCommand( kCmdOpen );
  1003.     }
  1004.     gMenuBar->EnableCommand( kCmdQuit );
  1005.         
  1006.     // enable the printing items if there is a printer and the front window
  1007.     // supports printing.
  1008.     
  1009.     if ( itsPrinter )
  1010.     {
  1011.         gMenuBar->EnableCommand( kCmdPageSetup );
  1012.     
  1013.         ZWindow*    fWindow = GetFrontWindow();
  1014.         
  1015.         if (fWindow && fWindow->IsPrintable())
  1016.             gMenuBar->EnableCommand( kCmdPrint );
  1017.     }    
  1018.     
  1019.     // edit:
  1020.     
  1021.     UpdateUndo();
  1022.     gMenuBar->EnableCommand( kCmdDoPreferences );
  1023. }
  1024.  
  1025.  
  1026. /*-------------------------------***  DOPAGESETUP  ***----------------------------------*/
  1027. /*
  1028. handle the Page Setup command    
  1029. ----------------------------------------------------------------------------------------*/
  1030.  
  1031. void    ZApplication::DoPageSetup()
  1032. {
  1033.     if ( itsPrinter )
  1034.     {
  1035.         gWindowManager->Deactivate();
  1036.         itsPrinter->PageSetUp();
  1037.         gWindowManager->Activate();
  1038.     }
  1039. }
  1040.  
  1041. /*----------------------------------***  DOPRINT  ***-----------------------------------*/
  1042. /*
  1043. handle the Print command    
  1044. ----------------------------------------------------------------------------------------*/
  1045.  
  1046. void    ZApplication::DoPrint()
  1047. {
  1048.     if ( itsPrinter )
  1049.     {
  1050.         ZWindow*    aWindow = GetFrontWindow();
  1051.         
  1052.         gWindowManager->Deactivate();
  1053.         itsPrinter->Print( aWindow );
  1054.         gWindowManager->Activate();
  1055.     }
  1056. }
  1057.  
  1058.  
  1059. /*---------------------------------***  ABOUTBOX  ***-----------------------------------*/
  1060. /*    
  1061. display the application's about box
  1062. ----------------------------------------------------------------------------------------*/
  1063.  
  1064. void    ZApplication::AboutBox()
  1065. {
  1066.     gWindowManager->DeactivateForDialog( kAboutBoxID, TRUE );
  1067.     (void) Alert( kAboutBoxID, NULL );
  1068.     gWindowManager->Activate();
  1069. }
  1070.  
  1071.  
  1072. /*----------------------------------***  SETTASK  ***-----------------------------------*/
  1073. /*    
  1074. update the current global undo task
  1075. ----------------------------------------------------------------------------------------*/
  1076.  
  1077. void    ZApplication::SetTask( ZUndoTask* aTask )
  1078. {
  1079.     if ( curUndoTask )
  1080.         ForgetObject( curUndoTask );
  1081.         
  1082.     curUndoTask = aTask;
  1083. }
  1084.  
  1085. /*--------------------------------***  UPDATEUNDO  ***----------------------------------*/
  1086. /*    
  1087. update the Undo menu item to reflect the current undo task
  1088. ----------------------------------------------------------------------------------------*/
  1089.  
  1090. void    ZApplication::UpdateUndo()
  1091. {
  1092.     Str255        undoMenuStr;
  1093.     
  1094.     if ( curUndoTask )
  1095.     {
  1096.         // a task available, so update the undo menu item to the task string.
  1097.         
  1098.         Str63        taskStr;
  1099.         
  1100.         if (curUndoTask->IsUndone())
  1101.             GetIndString( undoMenuStr, kMiscStrListID, kRedoStrIndex );
  1102.         else
  1103.             GetIndString( undoMenuStr, kMiscStrListID, kUndoStrIndex );
  1104.             
  1105.         // add the task name
  1106.         
  1107.         curUndoTask->GetTaskString( taskStr );
  1108.         ConcatPStrings( undoMenuStr, taskStr );
  1109.         
  1110.         // set the menu item to the task name
  1111.         
  1112.         gMenuBar->SetCommandText( kCmdUndo, undoMenuStr );
  1113.         
  1114.         // there is a task, but does it refer to the front window? If not,
  1115.         // then do not actually enable the command.
  1116.  
  1117.         if ((curUndoTask->GetUndoTarget() == GetFrontWindow()) &&
  1118.             (curUndoTask->GetUndoTarget() != NULL))
  1119.             gMenuBar->EnableCommand( kCmdUndo );
  1120.     }
  1121.     else
  1122.     {
  1123.         // if no task, just show a dimmed "Can't Undo"
  1124.         
  1125.         gMenuBar->SetCommandText( kCmdUndo, kMiscStrListID, kCantUndoStrIndex );
  1126.     }
  1127. }
  1128.  
  1129.  
  1130. #pragma mark -
  1131. /*-------------------------------***  INITMENUBAR  ***----------------------------------*/
  1132. /*    
  1133. set up the menubar object for managing the main menus
  1134. ----------------------------------------------------------------------------------------*/
  1135.  
  1136. void        ZApplication::InitMenuBar()
  1137. {
  1138.     // installs the menu bar. By default, we just install 'MBAR' ID = 128, which means you
  1139.     // don't need to override this to get other menus- just create the resources you want.
  1140.     
  1141.     FailNIL( gMenuBar = new ZMenuBar( kStdMenubarID ));
  1142.  
  1143.     gMenuBar->InitMenuBar();
  1144. }
  1145.  
  1146.  
  1147. /*---------------------------***  MOUSENOTINANYWINDOW  ***------------------------------*/
  1148. /*    
  1149. the mouse is not over any window. By default this just sets the default cursor shape, but
  1150. you can override this to get informed if you are interested in this fact.
  1151. ----------------------------------------------------------------------------------------*/
  1152.  
  1153. void    ZApplication::MouseNotInAnyWindow( const Point globalMouse )
  1154. {
  1155.     ResumeCursorAnimation();
  1156.     SetCursorShape( 0 );
  1157. }
  1158.  
  1159.  
  1160. /*--------------------------------***  MAKECLIPBOARD  ***-------------------------------*/
  1161. /*    
  1162.  
  1163. Make the clipboard object. By default, this is a ZClipboard.
  1164. ----------------------------------------------------------------------------------------*/
  1165.  
  1166. void        ZApplication::MakeClipboard()
  1167. {
  1168.     FailNIL( gClipboard = new ZClipboard());
  1169. }
  1170.  
  1171.  
  1172. /*------------------------------***  MAKEEVENTHANDLER  ***------------------------------*/
  1173. /*    
  1174.  
  1175. Make the event handler object. By default, this is a ZEventHandler. This also makes the
  1176. window manager, since that is functionally related to the event handler.
  1177. ----------------------------------------------------------------------------------------*/
  1178.  
  1179.  
  1180. void        ZApplication::MakeEventHandler()
  1181. {
  1182.     // make the event handler
  1183.     
  1184.     FailNIL( zEH = new ZEventHandler());
  1185.     
  1186.     // make the window manager for handling floating windows, etc.
  1187.     
  1188.     FailNIL( gWindowManager = new ZWindowManager());
  1189.     
  1190.     // install handlers for the four required events
  1191.     
  1192.     zEH->InstallApplescriptHandlers();        
  1193. }
  1194.  
  1195.  
  1196. /*--------------------------------***  MAKEPRINTER  ***---------------------------------*/
  1197. /*    
  1198.  
  1199. creates a new printer object for handling the print commands
  1200.  
  1201. ----------------------------------------------------------------------------------------*/
  1202.  
  1203. void        ZApplication::MakePrinter()
  1204. {
  1205.     #if PRINTING_ON
  1206.     
  1207.     try
  1208.     {
  1209.         FailNIL( itsPrinter = new ZPrinter());
  1210.     }
  1211.     catch( OSErr err )
  1212.     {
  1213.         itsPrinter = NULL;
  1214.         
  1215.         // note, to not have a printer is not fatal- in this case we carry on,
  1216.         // but print commands will be greyed out. This may happen if no printer
  1217.         // driver is selected in the chooser.
  1218.     }
  1219.     #endif
  1220. }
  1221.  
  1222. #pragma mark -
  1223.  
  1224. /*------------------------------***  MAKENEWWINDOW  ***---------------------------------*/
  1225. /*    
  1226.  
  1227. creates a new window object and initialises it. It does not show it yet. Normally you will
  1228. override this method to create your own useful kinds of windows. However, even if you don't
  1229. you'll still get a window that works. Eat your heart out Sprocket!
  1230. ----------------------------------------------------------------------------------------*/
  1231.  
  1232. void        ZApplication::MakeNewWindow()
  1233. {
  1234.     FailNIL( mostRecent = new ZWindow( this,  kUntitledWindowID ));
  1235.     
  1236.     try
  1237.     {
  1238.         mostRecent->InitZWindow();
  1239.     }
  1240.     catch( OSErr err )
  1241.     {
  1242.         ForgetObject( mostRecent );
  1243.         
  1244.         throw err;
  1245.     }
  1246. }
  1247.  
  1248.  
  1249. /*------------------------------***  OPENNEWWINDOW  ***---------------------------------*/
  1250. /*    
  1251.  
  1252. creates a new window object and initialises it. It then shows it and makes it active.
  1253.  
  1254. ----------------------------------------------------------------------------------------*/
  1255.  
  1256. void        ZApplication::OpenNewWindow()
  1257. {
  1258.     ZWindow*    theWindow;
  1259.     
  1260.     gMenuBar->SetZoomSourceToCommand( kCmdNew );
  1261.     
  1262.     theWindow = mostRecent = MakeNewWindowType( kDefaultWindowType );
  1263.     
  1264.     if ( theWindow )
  1265.     {
  1266.         theWindow->Place();
  1267.         theWindow->Select();
  1268.     }
  1269. }
  1270.  
  1271.  
  1272.  
  1273. /*----------------------------***  OPENNEWWINDOWTYPE  ***-------------------------------*/
  1274. /*    
  1275. create a new window object of the requisite type. This is the modern version of
  1276. OpenNewWindow(), but older code will work as expected.
  1277. ----------------------------------------------------------------------------------------*/
  1278.  
  1279. ZWindow*    ZApplication::OpenNewWindowType( OSType aType )
  1280. {
  1281.     ZWindow*    theWindow;
  1282.     
  1283.     gMenuBar->SetZoomSourceToCommand( kCmdNew );
  1284.  
  1285.     theWindow = mostRecent = MakeNewWindowType( aType );
  1286.     
  1287.     if ( theWindow )
  1288.     {
  1289.         theWindow->Place();
  1290.         theWindow->Select();
  1291.     }
  1292.     
  1293.     return theWindow;
  1294. }
  1295.  
  1296.  
  1297. /*---------------------------------***  CLOSEALL  ***-----------------------------------*/
  1298. /*    
  1299.  
  1300. closes all of the application's windows. If one refuses to close, this will abort a Quit.
  1301. This iterates backwards through its underlings list looking for windows, rather than
  1302. relying on the window lists, since windows will also close their own underling windows so
  1303. the whole commander tree is cleaned up properly. If your app organises things differently,
  1304. you may need to override this.
  1305. ----------------------------------------------------------------------------------------*/
  1306.  
  1307. void    ZApplication::CloseAll( Boolean closeFloaters )
  1308. {
  1309.     long        i;
  1310.     ZWindow*    aWindow;
  1311.     
  1312.     if ( itsUnderlings )
  1313.     {
  1314.         i = itsUnderlings->CountItems();
  1315.         
  1316.         while( i )
  1317.         {
  1318.             aWindow = dynamic_cast<ZWindow*>( itsUnderlings->GetObject( i-- ));
  1319.             
  1320.             if ( aWindow )
  1321.             { 
  1322.                 if ( phase == kQuitting )
  1323.                 {
  1324.                     if ( ! aWindow->Close( phase ))
  1325.                     {
  1326.                         phase = kRunning;
  1327.                         done = FALSE;
  1328.                         break;
  1329.                     }
  1330.                 }
  1331.                 else
  1332.                 {
  1333.                     if (! aWindow->NoAutoClose() && ( aWindow->Floats() == closeFloaters ))
  1334.                     {
  1335.                         if ( ! aWindow->Close( phase ))
  1336.                         {
  1337.                             phase = kRunning;
  1338.                             done = FALSE;
  1339.                             break;
  1340.                         }
  1341.                     }
  1342.                 }
  1343.             }
  1344.         }
  1345.     }
  1346. }
  1347.  
  1348. /*------------------------------***  GETFRONTWINDOW  ***--------------------------------*/
  1349. /*    
  1350.  
  1351. returns the active window object, if there is one
  1352.  
  1353. ----------------------------------------------------------------------------------------*/
  1354.  
  1355. ZWindow*    ZApplication::GetFrontWindow()
  1356. {
  1357.     return ( gWindowManager->GetTopWindow());
  1358. }
  1359.  
  1360.  
  1361. /*----------------------------------***  PICKFILE  ***----------------------------------*/
  1362. /*    
  1363.  
  1364. displays the standard file dialog for selecting a file, and returns its filespec.
  1365.  
  1366. ----------------------------------------------------------------------------------------*/
  1367.  
  1368.  
  1369. Boolean        ZApplication::PickFile( FSSpec* aFile, OSType* fType )
  1370. {
  1371.     // uses standard file to choose a file to open to a window. By default, no files types
  1372.     // are added to the list, which we here interpret to mean "show all files".
  1373.  
  1374. #if _USE_NAVIGATION_SERVICES
  1375.     // using navigation services, so we need to tackle things a little differently. The result
  1376.     // from this method is the same- namely a single filespec and a type.
  1377.     
  1378.     if ( gMacInfo.hasNavigationServices )
  1379.     {
  1380.         OSErr                theErr;
  1381.         NavReplyRecord        navReply;
  1382.         NavDialogOptions    navOptions;
  1383.         Boolean                result = FALSE;
  1384.         
  1385.         FailOSErr( NavGetDefaultDialogOptions( &navOptions ));
  1386.         
  1387.         GetName( navOptions.clientName );
  1388.         
  1389.         // only permit one selection here:
  1390.         
  1391.         navOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;
  1392.         gWindowManager->Deactivate();
  1393.         StopCursorAnimation();
  1394.         
  1395.         theErr = NavGetFile( NULL,
  1396.                              &navReply,
  1397.                              &navOptions,
  1398.                              gNavEventHandler,
  1399.                              NULL,
  1400.                              NULL,
  1401.                              (NavTypeListHandle) itsFileTypes,
  1402.                              (NavCallBackUserData) this );
  1403.                              
  1404.         gWindowManager->Activate();
  1405.                                 
  1406.         // extract info:
  1407.         
  1408.         if (( theErr == noErr ) && navReply.validRecord )
  1409.         {
  1410.             long    count, i;
  1411.             AEDesc    specDesc;
  1412.             FInfo    fi;
  1413.             
  1414.             FailOSErr( AECountItems( &navReply.selection, &count ));
  1415.             
  1416.             for( i = 1; i <= count; i++ )
  1417.             {
  1418.                 FailOSErr( AEGetNthDesc( &navReply.selection, i, typeFSS, NULL, &specDesc ));
  1419.             
  1420.                 BlockMoveData( *specDesc.dataHandle, aFile, sizeof( FSSpec ));
  1421.                 
  1422.                 // to get the type we need to do a FsGetFInfo:
  1423.                 
  1424.                 FSpGetFInfo( aFile, &fi );
  1425.                 *fType = fi.fdType;
  1426.             }
  1427.         
  1428.         
  1429.             result = TRUE;
  1430.         }
  1431.         else
  1432.             result = FALSE;
  1433.             
  1434.         FailOSErr( NavDisposeReply( &navReply ));
  1435.         
  1436.         return result;
  1437.     }
  1438.     else
  1439.     {
  1440. #endif
  1441.  
  1442.     StandardFileReply    aReply;
  1443.     OSType*                listPtr;
  1444.     short                numTypes;
  1445.     
  1446.     HLock((Handle) itsFileTypes );
  1447.     numTypes = (*itsFileTypes )->osTypeCount;
  1448.     listPtr = &(*itsFileTypes)->osType[0];
  1449.     
  1450.     // if no types in the list, show all of them
  1451.     
  1452.     if( numTypes <= 0 )
  1453.         numTypes = -1;
  1454.     
  1455.     StopCursorAnimation();
  1456.     gWindowManager->DeactivateForDialog( sfGetDialogID );
  1457.     
  1458.     // display the dialog
  1459.     StandardGetFile( NULL, numTypes, listPtr, &aReply );
  1460.     HUnlock((Handle) itsFileTypes );
  1461.     
  1462.     gWindowManager->Activate();
  1463.     
  1464.     if ( aReply.sfGood )
  1465.     {
  1466.         *aFile = aReply.sfFile;
  1467.         *fType = aReply.sfType;
  1468.         return TRUE;
  1469.     }
  1470.     else
  1471.         return FALSE;
  1472.  
  1473. #if _USE_NAVIGATION_SERVICES
  1474.     }
  1475. #endif
  1476. }
  1477.  
  1478.  
  1479. /*----------------------------------***  OPENFILE  ***----------------------------------*/
  1480. /*    
  1481.  
  1482. creates a new window and asks it to open the file. It then shows the window and activates it.
  1483. If your application doesn't have a classic "document" interface, but e.g. simply processes
  1484. files dropped on the application, you can override this method to process the files. It will
  1485. be called once for each file dropped on the application that we know how to open.
  1486. ----------------------------------------------------------------------------------------*/
  1487.  
  1488. void        ZApplication::OpenFile( const FSSpec& aFile, const OSType fType, Boolean isStationery )
  1489. {
  1490.     // opens the file into a new window. This is equivalent to OpenNewWindow, but
  1491.     // for when the user chose a file with the Open command
  1492.     
  1493.     ZWindow*    aWindow = NULL;
  1494.     
  1495.     SetWatchCursor();
  1496.     aWindow = mostRecent = MakeNewWindowType( fType );
  1497.     
  1498.     // window created, so ask it to open the chosen file
  1499.     
  1500.     if ( aWindow )
  1501.     {
  1502.         try
  1503.         {
  1504.             aWindow->SetFile( aFile );
  1505.             aWindow->OpenFile( fType, isStationery );
  1506.             aWindow->Place();
  1507.             aWindow->Select();
  1508.         }
  1509.         catch( OSErr err )
  1510.         {
  1511.             ForgetObject( aWindow );
  1512.             throw err;
  1513.         }
  1514.     }
  1515. }
  1516.  
  1517.  
  1518. /*--------------------------------***  ADDFILETYPE  ***---------------------------------*/
  1519. /*    
  1520. adds <aType> to the list of types this application will show in the Open dialog. You can
  1521. call this for each type your application can open. This ignores duplicates.
  1522. -----------------------------------------------------------------------------------------*/
  1523.  
  1524. void        ZApplication::AddFileType( const OSType aType )
  1525. {
  1526.     // adds the file type to the list of types, if not already there
  1527.     
  1528.     short    nTypes, i;
  1529.     
  1530.     nTypes = (*itsFileTypes)->osTypeCount;
  1531.     
  1532.     // check that the file type we are adding is unique in the list
  1533.     
  1534.     for ( i = 0; i < nTypes; i++ )
  1535.     {
  1536.         if ((*itsFileTypes)->osType[i] == aType )
  1537.             return;
  1538.     }
  1539.     
  1540.     // if we are still here, type is unique, so append it
  1541.     // first grow the handle
  1542.     
  1543.     SetHandleSize((Handle) itsFileTypes, GetHandleSize((Handle) itsFileTypes ) + sizeof( OSType ));
  1544.     FailMemError();
  1545.     
  1546.     // set the new entry
  1547.     
  1548.     (*itsFileTypes)->osType[ nTypes ] = aType;
  1549.     (*itsFileTypes)->osTypeCount++;
  1550. }
  1551.  
  1552.  
  1553. /*------------------------------***  CANOPENFILETYPE  ***--------------------------------*/
  1554. /*
  1555. return TRUE if this application can open files of the given type. The default method
  1556. determines if the type is in the fileType list or not. If the list is empty, this always
  1557. return TRUE.    
  1558. -----------------------------------------------------------------------------------------*/
  1559.  
  1560. Boolean        ZApplication::CanOpenFileType( const OSType aType )
  1561. {
  1562.     short    nTypes, i;
  1563.     
  1564.     nTypes = (*itsFileTypes)->osTypeCount;
  1565.     
  1566.     if ( nTypes > 0 )
  1567.     {
  1568.         for ( i = 0; i < nTypes; i++ )
  1569.         {
  1570.             if ( (*itsFileTypes)->osType[i] == aType )
  1571.                 return TRUE;
  1572.         }
  1573.         
  1574.         return FALSE;
  1575.     }
  1576.     else
  1577.         return TRUE;
  1578. }
  1579.  
  1580.  
  1581. /*------------------------------***  REGISTERCLASSES  ***-------------------------------*/
  1582. /*    
  1583. in order to use persistent objects, classes must be registered. You only need to register
  1584. classes that you will want to create from a stream. By default this registers those
  1585. required classes that could be constructed from a stream- override this to extend the
  1586. range of classes, but always call the inherited method.
  1587. ----------------------------------------------------------------------------------------*/
  1588.  
  1589. void        ZApplication::RegisterClasses()
  1590. {
  1591. #if _MACZOOP_STREAMS
  1592.  
  1593.     FailNIL( gClasses );
  1594.     
  1595.     REGISTERCLASS( ZComrade );
  1596.     REGISTERCLASS( ZCommander );
  1597.     REGISTERCLASS( ZArray );
  1598.     REGISTERCLASS( ZWindow );
  1599.     REGISTERCLASS( ZApplication );
  1600.     REGISTERCLASS( ZObjectList );
  1601.  
  1602. #endif
  1603. }
  1604.  
  1605.  
  1606.  
  1607. #pragma mark -
  1608. /*---------------------------------***  ZGROWFUNC  ***----------------------------------*/
  1609. /*    
  1610.  
  1611. memory manager callback proc.
  1612. ----------------------------------------------------------------------------------------*/
  1613.  
  1614. static pascal long ZGrowFunc( Size bytesShort )
  1615. {
  1616.     Boolean bytesFreed;
  1617.     Size    growBytes;
  1618.     
  1619.     // call application object to free some memory
  1620.     try
  1621.     {
  1622.         bytesFreed = gApplication->MemoryShortage( bytesShort );
  1623.         
  1624.         // try to compact and purge the memory after the
  1625.         // application has done something to free some up.
  1626.         
  1627.         (void) MaxMem( &growBytes );
  1628.     }
  1629.     catch( OSErr err )
  1630.     {
  1631.         bytesFreed = 0;
  1632.     }
  1633.     
  1634.     return bytesFreed;
  1635. }
  1636.  
  1637.  
  1638. /******************************************************************************
  1639.  CopyPString
  1640.  
  1641.         Copy a Pascal string
  1642.  ******************************************************************************/
  1643.  
  1644. void    CopyPString( ConstStr255Param srcString, Str255 destString )
  1645. {
  1646.     BlockMoveData( srcString, destString, MIN( srcString[0] + 1, 255 ));
  1647. }
  1648.  
  1649.  
  1650. /******************************************************************************
  1651.  ConcatPStrings
  1652.  
  1653.         Concatenate two Pascal strings by attaching the second string on
  1654.         the end of the first string.
  1655.  ******************************************************************************/
  1656.  
  1657. void    ConcatPStrings( Str255 root, ConstStr255Param append )
  1658. {
  1659.     short charsToCopy;
  1660.  
  1661.     // Truncate if concatenated string would be longer than 255 chars.
  1662.  
  1663.     charsToCopy = MIN( append[0], 255 - root[0]);
  1664.     BlockMoveData( append + 1, root + root[0] + 1, (long) charsToCopy);
  1665.     root[0] += charsToCopy;
  1666. }
  1667.  
  1668.  
  1669. /******************************************************************************
  1670.  CopyPStringTrunc
  1671.  
  1672.         Copy a Pascal string, limiting length to <ccLim> characters. This is
  1673.         handy for copying a Str255 to a shorter type, e.g. Str31.
  1674.  ******************************************************************************/
  1675.  
  1676. void    CopyPStringTrunc( ConstStr255Param srcString, Str255 destString, unsigned char ccLim )
  1677. {
  1678.     BlockMoveData( srcString, destString, MIN( srcString[0] + 1, ccLim + 1 ));
  1679.     destString[0] = MIN( srcString[0], ccLim );
  1680. }
  1681.  
  1682.  
  1683. /******************************************************************************
  1684.  ConcatPStringsTrunc
  1685.  
  1686.         Concatenate two Pascal strings by attaching the second string on
  1687.         the end of the first string, but limiting the overall length to <ccLim>
  1688.  ******************************************************************************/
  1689.  
  1690. void    ConcatPStringsTrunc( Str255 root, ConstStr255Param append, unsigned char ccLim )
  1691. {
  1692.     short charsToCopy;
  1693.  
  1694.     // Truncate if concatenated string would be longer than <ccLim> chars.
  1695.  
  1696.     charsToCopy = MIN( append[0], ccLim - root[0]);
  1697.     BlockMoveData( append + 1, root + root[0] + 1, (long) charsToCopy);
  1698.     root[0] += charsToCopy;
  1699. }
  1700.  
  1701.  
  1702.  
  1703. /*------------------------------***  RUNAPPLICATION  ***--------------------------------*/
  1704. /*    
  1705. standard function to run the application. Your main() function must make the relevant
  1706. ZApplication object, assign it to gApplication, then call this.
  1707. ----------------------------------------------------------------------------------------*/
  1708.  
  1709. void    RunApplication()
  1710. {
  1711.     if (gApplication)
  1712.     {    
  1713.         try
  1714.         {
  1715.             gApplication->InitMacZoop();            // initialise the whole kaboodle. This is
  1716.                                                     // NOT done by the constructor since you might
  1717.                                                     // want to override the initialisation.
  1718.         }
  1719.         catch( OSErr err )
  1720.         {
  1721.             // if an exception occurs during startup, the application cannot run, since
  1722.             // everything must be properly built and in place before handling events. In this
  1723.             // case we display a fatal alert message and exit.
  1724.             Str31    appName, errStr;
  1725.             
  1726.             gApplication->GetName( appName );
  1727.             NumToString( err, errStr );
  1728.             ParamText( appName, errStr, NULL, NULL );
  1729.         
  1730.             (void) Alert( kFatalStartupErrAlertID, NULL );
  1731.             ExitToShell();
  1732.         }
  1733.         
  1734.         // initialisation is now complete, so we can go ahead and run the thing
  1735.         
  1736.         do
  1737.         {
  1738.             gApplication->Run();                    // run the application until the user quits
  1739.         }
  1740.         while (! gApplication->Quit());                // try to quit
  1741.     }
  1742. }
  1743.  
  1744.  
  1745.  
  1746. Boolean    IsColourPort( GrafPtr aPort )
  1747. {
  1748.     return (( aPort->portBits.rowBytes & 0x8000 ) != 0 );
  1749. }
  1750.  
  1751.  
  1752. void    SetHiliteMode()
  1753. {
  1754.     LMSetHiliteMode( LMGetHiliteMode() & pHiliteBit );
  1755. }
  1756.  
  1757.  
  1758. /*----------------------------------***  MACHASDM  ***----------------------------------*/
  1759. /*    
  1760.  
  1761. static function returns TRUE if Drag Manager is present. Checks link on PowerMacs.
  1762. ----------------------------------------------------------------------------------------*/
  1763.  
  1764. Boolean        MacHasDM()
  1765. {
  1766.     // returns TRUE if drag manager is installed on this mac and is loaded by the CFM.
  1767.     
  1768.     Boolean hasDM = gMacInfo.hasDragManager;
  1769.     
  1770.     #if GENERATINGCFM 
  1771.         // check that the dragLib is actually loaded and linked
  1772.         
  1773.         hasDM = hasDM && ( NewDrag != (void*) kUnresolvedCFragSymbolAddress );
  1774.  
  1775.     #endif
  1776.  
  1777.     return hasDM;
  1778. }
  1779.  
  1780.  
  1781.  
  1782. /*--------------------------------***  NOTIFYALERT  ***---------------------------------*/
  1783. /*    
  1784. Works just like Alert(), except that if the app is in the background, the notification
  1785. manager is used to inform the user that the app requires attention.
  1786. ----------------------------------------------------------------------------------------*/
  1787.  
  1788. static NMRec    gNotification;
  1789. static Str255    gNotifyMessage;
  1790. static Boolean    gNotificationPosted = FALSE;
  1791.  
  1792. short        NotifyAlert( const short alertID, NTAlertFlags ntFlags )
  1793. {
  1794.     // basically: if ( background ):
  1795.     //                install notification
  1796.     //                wait for app foreground
  1797.     //                delete notification
  1798.     
  1799.     if ( ! gNotificationPosted )
  1800.     {
  1801.         if ( gApplication->InBackground())
  1802.         {
  1803.             // if an alert is to be displayed, set it up
  1804.             
  1805.             if ( ntFlags & ntAlertDisplayMessage )
  1806.             {
  1807.                 // build a string that says "The application “<name>” requires your attention.
  1808.                 // Please bring it to the front.
  1809.                 
  1810.                 Str255    appName;
  1811.                 
  1812.                 GetIndString( gNotifyMessage, kMiscStrListID, 17 );
  1813.                 gApplication->GetName( appName );
  1814.                 ConcatPStrings( gNotifyMessage, appName );
  1815.                 GetIndString( appName, kMiscStrListID, 18 );
  1816.                 ConcatPStrings( gNotifyMessage, appName );
  1817.                 
  1818.                 gNotification.nmStr = gNotifyMessage;
  1819.             }
  1820.             else
  1821.                 gNotification.nmStr = NULL;
  1822.             
  1823.             // set up any sound:
  1824.             
  1825.             if ( ntFlags & ntAlertPlaySound )
  1826.                 gNotification.nmSound = ( Handle ) -1L;
  1827.             else
  1828.                 gNotification.nmSound = NULL;
  1829.             
  1830.             Handle    appIconSuiteH;
  1831.             OSErr    theErr;
  1832.                 
  1833.             // set up icon and menu mark
  1834.             
  1835.             gNotification.qType = 8;
  1836.             gNotification.nmMark = TRUE;
  1837.             gNotification.nmResp = NULL;
  1838.             gNotification.nmRefCon = NULL;
  1839.             
  1840.             theErr = GetIconSuite( &appIconSuiteH, kApplicationIconSuiteID, svAllAvailableData );
  1841.             
  1842.             if ( theErr == noErr )
  1843.                 gNotification.nmIcon = appIconSuiteH;
  1844.             else
  1845.                 gNotification.nmIcon = NULL;
  1846.                 
  1847.             // install notification:
  1848.  
  1849.             NMInstall( &gNotification );
  1850.                 
  1851.             gNotificationPosted = TRUE;
  1852.             
  1853.             // handle events until we come back to the front:
  1854.         
  1855.             gApplication->WaitApplicationForeground();
  1856.             
  1857.             // we're back, so delete the notification
  1858.             
  1859.             NMRemove( &gNotification );
  1860.             gNotificationPosted = FALSE;
  1861.             
  1862.             if ( appIconSuiteH )
  1863.                 DisposeIconSuite( appIconSuiteH, FALSE );
  1864.         }
  1865.  
  1866.         return Alert( alertID, NULL );
  1867.     }
  1868.     else
  1869.     {
  1870.         // what can we do? This shouldn't arise in normal use, but just in case, we act as
  1871.         // though the user saw an alert and clicked OK. Not ideal, but since a notification
  1872.         // can't interrupt another, there's little choice.
  1873.         
  1874.         SysBeep( 1 );
  1875.         return ok;
  1876.     }
  1877. }
  1878.  
  1879. // header invariant version of Delay():
  1880.  
  1881. void    MZDelay( short ticks )
  1882. {
  1883.     #ifdef __COMPATIBILITY__
  1884.         long ignored;
  1885.     #else
  1886.         unsigned long ignored;
  1887.     #endif
  1888.     
  1889.     Delay( ticks, &ignored );
  1890. }
  1891.  
  1892.  
  1893.  
  1894. void    MZWait( unsigned short ticks )
  1895. {
  1896.     long    tc = TickCount() + ticks;
  1897.     
  1898.     while( TickCount() < tc )
  1899.         gApplication->Process1Event();
  1900. }
  1901.  
  1902.  
  1903.  
  1904. void    AssertErr( long lineNo, char* srcFile, char* reason, long val )
  1905. {
  1906. #if _DEBUG_
  1907.     
  1908.     Str32        lineStr, valStr;
  1909.     StringPtr    fileStr, reasonStr;
  1910.     
  1911.     NumToString( lineNo, lineStr );
  1912.     NumToString( val, valStr );
  1913.     fileStr = c2pstr( srcFile );
  1914.     reasonStr = c2pstr( reason );
  1915.     
  1916.     ParamText( lineStr, fileStr, reasonStr, valStr );
  1917.     StopCursorAnimation();
  1918.     
  1919.     (void) Alert( kAssertionAlertID, NULL );
  1920.     
  1921.     FailOSErr( kSilentErr );
  1922.     
  1923. #endif
  1924. }
  1925.  
  1926.  
  1927. #if _USE_NAVIGATION_SERVICES
  1928. pascal void    ZNavEventCallback(     NavEventCallbackMessage cbMessage,
  1929.                                 NavCBRecPtr    cbParams,
  1930.                                 NavCallBackUserData    cbData )
  1931. {
  1932.     switch ( cbMessage )
  1933.     {
  1934.         case kNavCBEvent:
  1935.             // we are only interested in updates actually
  1936.             
  1937.             if ( cbParams->eventData.event->what == updateEvt )
  1938.                 gApplication->Process1Event( cbParams->eventData.event );
  1939.             break;
  1940.             
  1941.         default:
  1942.             break;
  1943.     
  1944.     
  1945.     }
  1946. }
  1947.  
  1948. #endif
  1949.